#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "goatvfs.h"

char *strip_wspace(char *str);
char **tokenize(char *str);
void cmdproc(int argc, char **argv);

int main(void)
{
	int i;
	char buf[512];

	printf("welcome to packvfs shell\n");

	fputs("> ", stdout);
	fflush(stdout);

	while(fgets(buf, sizeof buf, stdin)) {
		char *inp = strip_wspace(buf);

		if(inp && *inp && *inp != '#') {
			char **tokens = tokenize(inp);
			for(i=0; tokens[i]; i++);

			cmdproc(i, tokens);
		}

		fputs("> ", stdout);
		fflush(stdout);
	}
	putchar('\n');

	return 0;
}

char *strip_wspace(char *str)
{
	char *end = str + strlen(str) - 1;

	while(*str && isspace(*str)) str++;

	while(isspace(*end)) end--;
	end[1] = 0;

	return str;
}

#define SEP		" \t\v\n\r"

char **tokenize(char *str)
{
	int idx = 1;
	static char *argv[256];

	argv[0] = strtok(str, SEP);
	while((argv[idx] = strtok(0, SEP))) idx++;

	argv[idx] = 0;
	return argv;
}

void cmd_chdir(int argc, char **argv);
void cmd_mkdir(int argc, char **argv);
void cmd_list(int argc, char **argv);
void cmd_train(int argc, char **argv);
void cmd_cat(int argc, char **argv);
void cmd_pwd(int argc, char **argv);
void cmd_mount(int argc, char **argv);
void cmd_umount(int argc, char **argv);

static struct { const char *cmd; void (*proc)(int, char**); } cmdlist[] = {
	{"cd", cmd_chdir},
	{"chdir", cmd_chdir},
	{"mkdir", cmd_mkdir},
	{"ls", cmd_list},
	{"sl", cmd_train},
	{"cat", cmd_cat},
	{"pwd", cmd_pwd},
	{"mount", cmd_mount},
	{"umount", cmd_umount},
	{0, 0}
};

void cmdproc(int argc, char **argv)
{
	int i;

	for(i=0; cmdlist[i].cmd; i++) {
		if(strcmp(argv[0], cmdlist[i].cmd) == 0) {
			cmdlist[i].proc(argc, argv);
			return;
		}
	}

	fprintf(stderr, "unknown command: %s\n", argv[0]);
}

void cmd_chdir(int argc, char **argv)
{
	if(argv[1]) {
		if(gvfs_chdir(argv[1]) == -1) {
			fprintf(stderr, "failed to change into directory: %s\n", argv[1]);
		}
	} else {
		fprintf(stderr, "syntax: cd <path>\n");
	}
}

void cmd_mkdir(int argc, char **argv)
{
	if(!argv[1]) {
		fprintf(stderr, "usage: mkdir <path to new directory>\n");
		return;
	}
	if(gvfs_mkdir(argv[1], 0775) == -1) {
		fprintf(stderr, "failed to create directory: %s\n", argv[1]);
	}
}

static int lscmp(const void *a, const void *b)
{
	return strcmp(*(char**)a, *(char**)b);
}

void cmd_list(int argc, char **argv)
{
	char *dirpath = (char*)".";
	GVFS_DIR *dir;
	struct gvfs_dirent *dent;
	char **entries;
	int i, num_entries = 0;

	if(argv[1]) {
		dirpath = argv[1];
	}

	if(!(dir = gvfs_opendir(dirpath))) {
		fprintf(stderr, "failed to open directory\n");
		return;
	}

	while((dent = gvfs_readdir(dir))) {
		num_entries++;
	}

	entries = new char*[num_entries * sizeof *entries];

	i = 0;
	gvfs_rewinddir(dir);
	while((dent = gvfs_readdir(dir))) {
		entries[i++] = strdup(dent->d_name);
	}
	gvfs_closedir(dir);

	qsort(entries, num_entries, sizeof *entries, lscmp);
			/*(int (*)(const void*, const void*))strcmp);*/

	for(i=0; i<num_entries; i++) {
		puts(entries[i]);
		free(entries[i]);
	}
	delete [] entries;
}

void cmd_train(int argc, char **argv)
{
	printf("sorry, we're all out of trains at the moment\n");
}

void cmd_cat(int argc, char **argv)
{
	char buf[512];

	for(int i=1; i<argc; i++) {
		GVFS_FILE *fp = gvfs_fopen(argv[i], "rb");
		if(!fp) {
			fprintf(stderr, "failed to open file: %s\n", argv[i]);
			continue;
		}

		size_t sz;
		while((sz = gvfs_fread(buf, 1, sizeof buf, fp)) > 0) {
			fwrite(buf, 1, sz, stdout);
		}
		fflush(stdout);
	}
}

void cmd_pwd(int argc, char **argv)
{
	printf("not implemented yet!\n");
}

void cmd_mount(int argc, char **argv)
{
	if(argc < 3) {
		fprintf(stderr, "usage: mount <mountpoint> <target>\n");
		return;
	}

	if(gvfs_mount(argv[1], argv[2]) == -1) {
		fprintf(stderr, "failed to mount %s to %s\n", argv[2], argv[1]);
	}
}

void cmd_umount(int argc, char **argv)
{
	if(argc < 2) {
		fprintf(stderr, "usage: umount <mountpoint>\n");
		return;
	}

	if(gvfs_umount(argv[1]) == -1) {
		fprintf(stderr, "failed to umount %s\n", argv[1]);
	}
}
